package com.dr.code.diff;

import lombok.extern.slf4j.Slf4j;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.revwalk.filter.RevFilter;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;

import static com.dr.code.diff.util.GitRepoUtil.COMMIT_ID_LENGTH;

@Slf4j
public class BranchDiffUtil {

    /**
     * 拉取指定分支到 refs/remotes/origin/xxx。
     * 若未配置远程 origin 或未传分支，则跳过 fetch。
     */
    public static void fetchBranches(Git git, String... branches) throws Exception {
        if (branches == null || branches.length == 0) {
            System.out.println("未指定需要 fetch 的分支，跳过 fetch。");
            return;
        }
        Repository repository = git.getRepository();
        if (!repository.getConfig().getSubsections("remote").contains("origin")) {
            System.out.println("未检测到远程仓库 'origin'，跳过 fetch，将使用本地分支进行比较。");
            return;
        }
        RefSpec[] refSpecs = new RefSpec[branches.length];
        for (int i = 0; i < branches.length; i++) {
            //如果是commit id则不需要 fetch
            if(COMMIT_ID_LENGTH == branches[i].length()){
                return;
            }
            refSpecs[i] = new RefSpec("refs/heads/" + branches[i] + ":refs/remotes/origin/" + branches[i]);
        }
        git.fetch()
                .setRemote("origin")
                .setRefSpecs(refSpecs)
                .call();
        System.out.println("Fetched branches: " + String.join(", ", branches));
    }


    /**
     * 对比模式
     * DIRECT: 直接比较 A 与 B
     * MERGE_BASE: 比较 B 相对 merge-base 的变更
     */
    public enum ComparisonMode {
        DIRECT,
        MERGE_BASE
    }

    /**
     * 展示两个分支之间的差异。
     * 分支解析优先远程（refs/remotes/origin/xxx），不存在时回退到本地（refs/heads/xxx）。
     */
    public static List<DiffEntry> getDiffBetweenBranches(Git git, String branchA, String branchB, ComparisonMode mode) throws Exception {
        Repository repository = git.getRepository();
        ObjectId branchAId = resolveBranchId(repository, branchA);
        ObjectId branchBId = resolveBranchId(repository, branchB);

        if (branchAId == null || branchBId == null) {
            throw new IllegalArgumentException("分支未找到，请确认分支存在（本地或远程）：" + branchA + ", " + branchB);
        }

        try (RevWalk walk = new RevWalk(repository)) {
            RevCommit commitA = walk.parseCommit(branchAId);
            RevCommit commitB = walk.parseCommit(branchBId);

            ObjectId oldTreeId;
            ObjectId newTreeId;

            if (mode == ComparisonMode.DIRECT) {
                oldTreeId = commitA.getTree().getId();
                newTreeId = commitB.getTree().getId();
            } else {
                walk.setRevFilter(RevFilter.MERGE_BASE);
                walk.markStart(commitA);
                walk.markStart(commitB);
                RevCommit mergeBase = walk.next();
                if (mergeBase == null) {
                    throw new IllegalStateException("未找到两个分支的共同祖先（merge-base）");
                }
                oldTreeId = mergeBase.getTree().getId();
                newTreeId = commitB.getTree().getId();
            }

            try (org.eclipse.jgit.lib.ObjectReader reader = repository.newObjectReader();
                 ByteArrayOutputStream out = new ByteArrayOutputStream();
                 DiffFormatter formatter = new DiffFormatter(out)) {

                CanonicalTreeParser oldTreeIter = new CanonicalTreeParser();
                oldTreeIter.reset(reader, oldTreeId);

                CanonicalTreeParser newTreeIter = new CanonicalTreeParser();
                newTreeIter.reset(reader, newTreeId);

                formatter.setRepository(repository);
                formatter.setDetectRenames(true);

                return git.diff()
                        .setOldTree(oldTreeIter)
                        .setNewTree(newTreeIter)
                        .call();

            }
        }
    }


    private static ObjectId resolveBranchId(Repository repository, String branch) throws IOException {
        if (branch == null || branch.trim().isEmpty()) {
            return null;
        }
        // 1) 直接解析：支持 40 位 commitId、tag 名称、完整引用等（repository.resolve 本身支持多种形式）
        ObjectId direct = repository.resolve(branch);
        if (direct != null) {
            return direct;
        }
        // 2) 常见引用前缀尝试：远程跟踪、本地分支、tag
        ObjectId id = repository.resolve("refs/remotes/origin/" + branch);
        if (id == null) {
            id = repository.resolve("refs/heads/" + branch);
        }
        if (id == null) {
            id = repository.resolve("refs/tags/" + branch);
        }
        return id;

    }

    // 简单测试：支持命令行参数
//    public static void main(String[] args) throws Exception {
//        String repoPath = args != null && args.length > 0 ? args[0] : "/Users/rayduan/test/app/git-test/code-diff/feature_20241204/";
//        String branchA = args != null && args.length > 1 ? args[1] : "master";
//        String branchB = args != null && args.length > 2 ? args[2] : "feature_20241204";
//        ComparisonMode mode = (args != null && args.length > 3 && "DIRECT".equalsIgnoreCase(args[3]))
//                ? ComparisonMode.DIRECT : ComparisonMode.MERGE_BASE;
//
//        BranchDiffUtil diffUtil = new BranchDiffUtil(repoPath);
//        try {
//            diffUtil.fetchBranches(branchA, branchB);
//            diffUtil.showDiffBetweenBranches(branchA, branchB, mode);
//        } finally {
//            diffUtil.close();
//        }
//    }
}